package com.klikmee.sms.core.base;

import java.text.MessageFormat;
import java.util.List;

import org.apache.log4j.Logger;

import com.klikmee.sms.cef.core.ServiceCommunicator;
import com.klikmee.sms.core.base.creators.BulkMessageCreator;
import com.klikmee.sms.core.base.creators.MessageCreator;
import com.klikmee.sms.core.base.dao.IncomingMessageFacadeLocal;
import com.klikmee.sms.core.base.dao.ServiceChargingCategoryDAO;
import com.klikmee.sms.core.base.util.Constants;
import com.klikmee.sms.core.base.util.States;
import com.klikmee.sms.core.commons.events.AppEvent;
import com.klikmee.sms.core.commons.events.Dispatcher;
import com.klikmee.sms.core.model.IncomingMessage;
import com.klikmee.sms.core.model.Service;
import com.klikmee.sms.core.model.ServiceChargingCategory;

/**
 * @author makcro
 * 
 */
public class SimpleServiceCommunicatorImpl implements ServiceCommunicator {

	/**
	 * Core instance used to enqueue outgoing messages
	 */
	protected Core							core;

	/**
	 * Component responsible for message creation
	 */
	protected MessageCreator				messageCreator;

	/**
	 * Component responsible for bulk message creation
	 */
	protected BulkMessageCreator			bulkMessageCreator;

	/**
	 * Incoming message DAO
	 */
	protected IncomingMessageFacadeLocal	incommingMessageDAO;
	/**
	 * Charging DAO
	 */
	protected ServiceChargingCategoryDAO	chargingDAO;

	protected Dispatcher					dispatcher;
	/**
	 * Logger instance
	 */
	private Logger							logger	= Logger.getLogger( SimpleServiceCommunicatorImpl.class );

	public void setMessageCreator(MessageCreator messageCreator) {
		this.messageCreator = messageCreator;
	}

	public void setBulkMessageCreator(BulkMessageCreator bulkMessageCreator) {
		this.bulkMessageCreator = bulkMessageCreator;
	}

	public void setIncommingMessageDAO(IncomingMessageFacadeLocal incommingMessageDAO) {
		this.incommingMessageDAO = incommingMessageDAO;
	}

	public void setCore(Core core) {
		this.core = core;
	}

	public void setChargingDAO(ServiceChargingCategoryDAO chargingDAO) {
		this.chargingDAO = chargingDAO;
	}

	/**
	 * sends charged message to a single recipient, as response to particular
	 * user's request and updates incoming message state.
	 * 
	 * @param incommingMessageId
	 *            id of the original request message sent by user
	 * @param chargingId
	 *            id of charging code for this service
	 * @param responseText
	 *            text of the response message
	 * @param serviceFlag
	 *            flag of response message. This is used to update incoming
	 *            message state as it carries meaning of the response - 0
	 *            regular service response ( incoming message is semantically
	 *            correct) or 1 which means that this is a info message (usually
	 *            free of charge) and the incoming message is semantically
	 *            incorrect.
	 * @return id of sent message if everything is OK -1 if incoming message id
	 *         is wrong -2 if charging id is wrong
	 * 
	 */
	public Long sendMessage(Long incommingMessageId, Long serviceId, Long chargingId, String responseText, int serviceFlag) {
		logger.debug( String.format( "Received sendMessage request from serviceId = [%1$s], charginId = [%2$s], text [%3$s]", serviceId, chargingId, responseText ) );

		// verify charging info
		int verificationResult = verifyChargingInformation( serviceId, chargingId );
		if (verificationResult != 0) {
			return new Long( verificationResult );
		}

		// find incomming message by id
		IncomingMessage incomingMessage = incommingMessageDAO.find( incommingMessageId );
		if (incomingMessage == null) {
			return new Long( -2 );
		}

		// find charging category
		ServiceChargingCategory chargingCategory = chargingDAO.find( chargingId );
		if (chargingCategory == null) {
			return new Long( -3 );
		}

		logger.debug( "Request verification succeeded, creating outgoing message." );
		Long responseId = messageCreator.createResponse( incomingMessage, chargingCategory, responseText );

		// notify about created response
		AppEvent appEvent = new AppEvent( EventManager.OUTGOING_MESSAGE_ENQUEUED );
		appEvent.setData( EventParams.INCOMING_MESSAGE, incomingMessage );
		appEvent.setData( EventParams.SERVICE_FLAG, serviceFlag );
		this.dispatcher.dispatch( appEvent, false );

		return responseId;
	}

	/**
	 * Verifies bulk request and instructs {@link BulkMessageCreator} to produce
	 * bulk messages.
	 * 
	 * @param bulkMessageText
	 *            text of bulk message
	 * @param phoneNumbers
	 *            bulk receivers
	 * 
	 * @param signatureText
	 *            bulk signature
	 * @param serviceId
	 *            id of the service which sends bulk request
	 * @param chargingId
	 *            charging id to use with this bulk request
	 * @return number of produced bulk messages
	 */
	public int sendBulkMessage(String bulkMessageText, List<String> phoneNumbers, String signatureText, Long serviceId, Long chargingId) {
		// verify charging info
		int verificationResult = verifyChargingInformation( serviceId, chargingId );

		if (verificationResult != 0) {
			return verificationResult;
		}
		logger.debug( MessageFormat.format( "Bulk request verification succeeded, instructing bulk creator to produce [{0}] bulk messag(s)..", phoneNumbers.size() ) );

		ServiceChargingCategory chargingCategory = chargingDAO.find( chargingId );

		int numberOfSentBulkMessages = bulkMessageCreator.createBulkMessage( bulkMessageText, phoneNumbers, signatureText, chargingCategory );
		logger.debug( MessageFormat.format( "Creator produced [{0}] bulk message(s)", numberOfSentBulkMessages ) );

		return numberOfSentBulkMessages;
	}

	public int verifyChargingInformation(Long serviceId, Long chargingCategoryId) {

		// create dummy obj
		Service tmpService = new Service();
		tmpService.setId( serviceId );

		ServiceChargingCategory tmpChargingCategory = new ServiceChargingCategory();
		tmpChargingCategory.setId( chargingCategoryId );

		List<Service> servicesList = core.getServiceDefinitions();
		for (Service service : servicesList) {
			// first match service
			if (service.equals( tmpService )) {
				// then check is service is related to charging category
				if (service.getServiceChargingCategoryList().contains( tmpChargingCategory )) {
					return 0;
				}
				else {
					return -1;
				}
			}
		}
		// if no service is matched return -1
		return -1;
	}

	public void setDispatcher(Dispatcher dispatcher) {
		this.dispatcher = dispatcher;
	}

}
